package zfile

import (
	"archive/zip"
	"bytes"
	"errors"
	"fmt"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

// DirExists 判断文件或目录是否存在
func DirExists(path string) bool {
	var result = false
	if _, err := os.Stat(path); err == nil {
		result = true
	}
	return result
}

// CreateDirectory 创建目录
func CreateDirectory(path string) bool {
	err := os.MkdirAll(path, 07777)
	if err == nil {
		return true
	} else {
		return false
	}
}

// DeleteDirectory 删除目录或文件
func DeleteDirectory(path string) bool {
	if e := os.RemoveAll(path); e == nil {
		return true
	}
	return false
}

// GetCurrentDirectory 获取当前目录
func GetCurrentDirectory() string {
	dir, _ := filepath.Abs(filepath.Dir(os.Args[0]))
	return dir
}

// ReadFile 读取文件
func ReadFile(filePath string) ([]byte, error) {
	var f *os.File
	var err error
	if f, err = os.Open(filePath); err != nil {
		return nil, err
	}
	defer func() {
		err = f.Close()
	}()
	return io.ReadAll(f)
}

// WriteFile 写文件
func WriteFile(fileName string, data []byte) bool {
	var err error
	if err = os.WriteFile(fileName, data, 0666); err == nil {
		return true
	} else {
		return false
	}
}

// WriteTextFile 追加，写文本文件
func WriteTextFile(path, data string) bool {
	var result = false
	var f *os.File
	var err error
	if f, err = os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666); err == nil {
		defer func() {
			err = f.Close()
		}()
		if _, err = f.WriteString(data); err == nil {
			result = true
		}
	}
	return result
}

// WriteTextCover 覆盖写入
func WriteTextCover(path, data string) bool {
	var result = false
	var f *os.File
	var err error
	if f, err = os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666); err == nil {
		defer func() {
			err = f.Close()
		}()
		if _, err = f.WriteString(data); err == nil {
			result = true
		}
	}
	return result
}

func CopyFile(src, dst string) (n int64, e error) {
	var (
		sourceFileStat os.FileInfo
		source         *os.File
		destination    *os.File
	)
	if sourceFileStat, e = os.Stat(src); e != nil {
		return
	}
	if !sourceFileStat.Mode().IsRegular() {
		e = fmt.Errorf("%s is not a regular file", src)
		return
	}

	if source, e = os.Open(src); e != nil {
		return
	}
	defer func() {
		e = source.Close()
	}()

	if destination, e = os.Create(dst); e != nil {
		return
	}
	defer func() {
		e = destination.Close()
	}()

	n, e = io.Copy(destination, source)
	return
}

// ShellCmd 执行shell命令
// args 需要执行的命令及参数
// input 需要输入的参数
func ShellCmd(args []string, input string) (ret string) {
	const empty = ""
	var (
		e     error
		stdin io.WriteCloser
		cmd   *exec.Cmd
	)
	argsSize := len(args)
	if argsSize == 0 {
		return fmt.Sprintf("命令参数不能为空")
	}

	// 构建命令（以sudo为例，其他需要交互的命令类似）
	if argsSize > 1 {
		cmd = exec.Command(args[0], args[1:]...)
	} else {
		cmd = exec.Command(args[0])
	}

	// 捕获命令的输出（合并stdout和stderr）
	var outputBuf bytes.Buffer
	cmd.Stdout = &outputBuf
	cmd.Stderr = &outputBuf
	cmd.Env = append(os.Environ(), "DISPLAY=:0.0")
	// 创建管道用于向命令发送输入
	if input != empty {
		defer func() {
			_ = stdin.Close()
		}()
		if stdin, e = cmd.StdinPipe(); e != nil {
			return fmt.Sprintf("创建输入管道失败: %v", e)
		}
	}
	// 启动命令
	if e = cmd.Start(); e != nil {
		return fmt.Sprintf("启动命令失败: %s\nerr:%s", e.Error(),outputBuf.String())
	}

	if input == empty {
		if e = cmd.Wait(); e != nil {
			return "return error:"+e.Error()+"\n" + outputBuf.String()
		}
		return strings.TrimSpace(outputBuf.String())
	}
	passwd := input + "\n"
	if _, e = io.WriteString(stdin, passwd); e != nil {
		fmt.Println(e)
	}

	// 等待命令完成
	if e = cmd.Wait(); e != nil {
		return e.Error()
	}
	return strings.TrimSpace(outputBuf.String())
}

// Unzip ZIP文件解压
func Unzip(dst, unFile string) (e error) {
	var archive *zip.ReadCloser
	if archive, e = zip.OpenReader(unFile); e != nil {
		return
	}
	defer func() {
		e = archive.Close()
	}()
	filePath := ""
	for _, f := range archive.File {
		filePath = filepath.Join(dst, f.Name)
		if !strings.HasPrefix(filePath, filepath.Clean(dst)+string(os.PathSeparator)) {
			return errors.New("invalid file unzip path")
		}
		if f.FileInfo().IsDir() {
			_ = os.MkdirAll(filePath, os.ModePerm)
			continue
		}

		if e = os.MkdirAll(filepath.Dir(filePath), os.ModePerm); e != nil {
			return
		}
		var dstFile *os.File
		if dstFile, e = os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode()); e != nil {
			return
		}
		var fileInArchive io.ReadCloser
		if fileInArchive, e = f.Open(); e != nil {
			return
		}

		if _, e = io.Copy(dstFile, fileInArchive); e != nil {
			return
		}
		_ = dstFile.Close()
		_ = fileInArchive.Close()
	}
	return nil
}
